unit area_core;

{$mode Delphi}

interface

uses
    Classes, SysUtils, Graphics, ExtCtrls, Dialogs, Math, area_pnt, area_trn;

const
	AREA_PNTS_MAX = 30;

type

	TDArea = class
	private
		area: TImage;
		area_pnts: array [0..AREA_PNTS_MAX - 1] of TDAPoint;
		area_pnts_cnt: Integer;
	public
		// Конструктор класса
		Constructor init(area_o: TImage);
		// Очистка области рисования
		procedure clear();
		// Отрисовка точки
		procedure render_pnt(area_pnt_o: TDAPoint; area_pnt_r: Integer = 2; area_pnt_c: TColor = clGreen);
		// Отрисовка треугольника
		procedure render_trn(area_trn_o: TDATriangle);
		// Вписать треугольник в область рисования
		procedure render_trn_fit(area_trn_o: TDATriangle);
		// Создание новой точки
		procedure pnts_add(area_pnt_o: TDAPoint);
		// Вставка точки
		procedure pnts_insert(area_pnt_o: TDAPoint; area_pnt_n: Integer);
		// Удаление точки по ее индексу
		procedure pnts_del_by_index(area_pnt_n: Integer);
		// Удаление всех точек
		procedure pnts_del_all();
		// Повторная отрисовка всех точек
		procedure pnts_render_all();
		// Получение точки по ее индексу
		function pnts_get_by_index(area_pnt_n: Integer) :TDAPoint;
		// Получение последней точки области рисования
		function pnts_get_last() :TDAPoint;
		// Получение количества точек области рисования
		function pnts_get_cnt() :Integer;
		// Получение максимального количества точек области рисования
		function pnts_get_cnt_max() :Integer;
		// Проверка видимости точки
		function pnts_get_visible(area_pnt_o: TDAPoint) :Boolean;
		// Координаты центра области рисования
		function pnts_get_area_cent() :TDAPoint;
		// Конвертирование точки в Декартову систему
		function pnts_conv(area_pnt_o: TDAPoint) :TDAPoint;
	end;

implementation

const
	PREC_V = -4;

// Конструктор класса
Constructor TDArea.init(area_o: TImage);
begin
	area := area_o;
	area_pnts_cnt := 0;
end;

// Очистка области рисования
procedure TDArea.clear();
begin
	with area.Canvas do
	begin
		Pen.Color := clGray;
		Brush.Color := clWhite;
		Rectangle(0, 0, area.Width, area.Height);
	end;
end;

// Отрисовка точки
procedure TDArea.render_pnt(area_pnt_o: TDAPoint; area_pnt_r: Integer = 2; area_pnt_c: TColor = clGreen);
var
	rad_ins, rad_out: Integer;
begin

	rad_ins := area_pnt_r;
	rad_out := area_pnt_r * 2;

	with area.Canvas do
	begin
		Pen.Color := clBlue;
		Brush.Color := clWhite;
		Ellipse(area_pnt_o.get_dcoord.X - rad_out, 
				area_pnt_o.get_dcoord.Y - rad_out, 
				area_pnt_o.get_dcoord.X + rad_out, 
				area_pnt_o.get_dcoord.Y + rad_out);
		Pen.Color := clBlue;
		Brush.Color := area_pnt_c;
		Ellipse(area_pnt_o.get_dcoord.X - rad_ins, 
				area_pnt_o.get_dcoord.Y - rad_ins, 
				area_pnt_o.get_dcoord.X + rad_ins, 
				area_pnt_o.get_dcoord.Y + rad_ins);
	end;
end;

// Отрисовка треугольника
procedure TDArea.render_trn(area_trn_o: TDATriangle);
var
	bisc_n: Integer;
begin
	with area.Canvas do
	begin
		// Отрисовка ребер
		Pen.Color := clBlue;
		MoveTo(area_trn_o.get_head_pnt(0).get_dcoord);
		LineTo(area_trn_o.get_head_pnt(1).get_dcoord);
		LineTo(area_trn_o.get_head_pnt(2).get_dcoord);
		LineTo(area_trn_o.get_head_pnt(0).get_dcoord);
		// Отрисовка минимальной биссектрисы
		bisc_n := area_trn_o.get_bisc_min();
		Pen.Color := clRed;
		MoveTo(area_trn_o.get_head_pnt(bisc_n).get_dcoord);
		LineTo(area_trn_o.get_bisc_pnt(bisc_n).get_dcoord);
		render_pnt(area_trn_o.get_bisc_pnt(bisc_n));
	end;
end;

// Подфункция вычисления минимального числа
function sub_get_min(val_a, val_b, val_c: Real) :Integer;
var
    min_n: Integer;
begin
    min_n := 0;

    if val_b < val_a then
    begin
        val_a := val_b;
        min_n := 1;
    end;

    if val_c < val_a then
        min_n := 2;

    sub_get_min := min_n;
end;

// Подфункция вычисления максимального числа
function sub_get_max(val_a, val_b, val_c: Real) :Integer;
var
    max_n: Integer;
begin
    max_n := 0;

    if val_b > val_a then
    begin
        val_a := val_b;
        max_n := 1;
    end;

    if val_c > val_a then
        max_n := 2;

    sub_get_max := max_n;
end;

// Координаты центра области рисования
function TDArea.pnts_get_area_cent() :TDAPoint;
var
	area_cent_o: TDAPoint;
begin
	area_cent_o := TDAPoint.init(area.width / 2, area.Height / 2);
	pnts_get_area_cent := area_cent_o;
end;

// Вписать треугольник в область рисования
procedure TDArea.render_trn_fit(area_trn_o: TDATriangle);
var
	// Вершины треугольника
	head_f, head_s, head_t: TDAPoint;
	// Центр треугольника
	area_trn_cent: TDAPoint;
	// Центр области рисования
	area_cent: TDAPoint;
	// Параметры смещения
	move_x, move_y: Real;
	// Координаты
	x_min, x_max: Real;
	y_min, y_max: Real;
	scale_x, scale_y, scale: Real;
	// Временные переменные
	cache: Integer;
begin

	// Вершины треугольника
	head_f := area_trn_o.get_head_pnt(0);
	head_s := area_trn_o.get_head_pnt(1);
	head_t := area_trn_o.get_head_pnt(2);

	// Поиск минимальных координат
	cache := sub_get_min(head_f.get_rcoord.X, 
						 head_s.get_rcoord.X,
						 head_t.get_rcoord.X);
	x_min := area_trn_o.get_head_pnt(cache).get_rcoord.X;

	cache := sub_get_max(head_f.get_rcoord.X, 
						 head_s.get_rcoord.X, 
						 head_t.get_rcoord.X);
	x_max := area_trn_o.get_head_pnt(cache).get_rcoord.X;

	cache := sub_get_min(head_f.get_rcoord.Y, 
						 head_s.get_rcoord.Y, 
						 head_t.get_rcoord.Y);
	y_min := area_trn_o.get_head_pnt(cache).get_rcoord.Y;

	cache := sub_get_max(head_f.get_rcoord.Y, 
						 head_s.get_rcoord.Y, 
						 head_t.get_rcoord.Y);
	y_max := area_trn_o.get_head_pnt(cache).get_rcoord.Y;

	// Вычисление коэффициента масштабирования
	scale_x := RoundTo(area.Width / (x_max - x_min), PREC_V);
	scale_y := RoundTo(area.Height / (y_max - y_min), PREC_V);

	if scale_y < scale_x then
		scale := scale_y
	else
		scale := scale_x;

	// Центр области рисования
	area_cent := pnts_get_area_cent();

	// Вычисление коэффициента смещения
	area_trn_cent := area_trn_o.get_cent_pnt();
	area_trn_cent := TDAPoint.init(area_trn_cent.get_rcoord.X * scale, 
								   area_trn_cent.get_rcoord.Y * scale);

	move_x := area_cent.get_rcoord.X - area_trn_cent.get_rcoord.X;
	move_y := area_cent.get_rcoord.Y - area_trn_cent.get_rcoord.Y;

	// Масштабирование треугольника
	with head_f do
		head_f := TDAPoint.init(get_rcoord.X * scale + move_x, 
								get_rcoord.Y * scale + move_y);
	with head_s do
		head_s := TDAPoint.init(get_rcoord.X * scale + move_x, 
								get_rcoord.Y * scale + move_y);
	with head_t do
		head_t := TDAPoint.init(get_rcoord.X * scale + move_x, 
								get_rcoord.Y * scale + move_y);


	// Сборка треугольника
	area_trn_o := TDATriangle.init(head_f, head_s, head_t);

	// Проверка смещения
	cache := sub_get_min(head_f.get_rcoord.X, 
						 head_s.get_rcoord.X,
						 head_t.get_rcoord.X);
	x_min := area_trn_o.get_head_pnt(cache).get_rcoord.X;

	cache := sub_get_max(head_f.get_rcoord.X, 
						 head_s.get_rcoord.X,
						 head_t.get_rcoord.X);
	x_max := area_trn_o.get_head_pnt(cache).get_rcoord.X;

	cache := sub_get_min(head_f.get_rcoord.Y, 
						 head_s.get_rcoord.Y, 
						 head_t.get_rcoord.Y);
	y_min := area_trn_o.get_head_pnt(cache).get_rcoord.Y;

	cache := sub_get_max(head_f.get_rcoord.Y, 
						 head_s.get_rcoord.Y, 
						 head_t.get_rcoord.Y);
	y_max := area_trn_o.get_head_pnt(cache).get_rcoord.Y;

	if x_min < 0 then move_x := -x_min else move_x := 0;
	if x_max > area.Width then move_x := area.Width - x_max;

	if y_min < 0 then move_y := -y_min else move_y := 0;
	if y_max > area.Height then move_y := area.Height - y_max;

	// Смещение треугольника
	if (move_x <> 0) or (move_y <> 0) then
	begin
		with head_f do
			head_f := TDAPoint.init(get_rcoord.X + move_x, 
									get_rcoord.Y + move_y);
		with head_s do
			head_s := TDAPoint.init(get_rcoord.X + move_x, 
									get_rcoord.Y + move_y);
		with head_t do
			head_t := TDAPoint.init(get_rcoord.X + move_x, 
									get_rcoord.Y + move_y);
	end;

	// Отрисовка треугольника
	area_trn_o := TDATriangle.init(head_f, head_s, head_t);
	render_trn(area_trn_o);
	render_pnt(head_f, 3, clRed);
	render_pnt(head_s, 3, clYellow);
	render_pnt(head_t, 3, clLime);
end;

// Создание новой точки
procedure TDArea.pnts_add(area_pnt_o: TDAPoint);
begin	
	area_pnts[area_pnts_cnt] := area_pnt_o;
	area_pnts_cnt := area_pnts_cnt + 1;
end;

// Вставка точки
procedure TDArea.pnts_insert(area_pnt_o: TDAPoint; area_pnt_n: Integer);
begin
	area_pnts[area_pnt_n] := area_pnt_o;
end;

// Удаление точки по ее индексу
procedure TDArea.pnts_del_by_index(area_pnt_n: Integer);
begin
	area_pnts[area_pnt_n] := area_pnts[area_pnts_cnt - 1];
	area_pnts_cnt := area_pnts_cnt - 1;
end;

// Удаление всех точек
procedure TDArea.pnts_del_all();
begin
	area_pnts_cnt := 0;
	clear();
end;

// Переотрисовка всех точек
procedure TDArea.pnts_render_all();
var
	I: Integer;
begin
	clear();
	if (area_pnts_cnt > 0) then
		for I := 0 to area_pnts_cnt - 1 do
			render_pnt(area_pnts[I]);
end;

// Получение точки по ее индексу
function TDArea.pnts_get_by_index(area_pnt_n: Integer): TDAPoint;
begin
	pnts_get_by_index := area_pnts[area_pnt_n];
end;

// Получение последней точки области рисования
function TDArea.pnts_get_last() :TDAPoint;
begin
	pnts_get_last := area_pnts[area_pnts_cnt - 1];
end;

// Получение количества точек области рисования
function TDArea.pnts_get_cnt() :Integer;
begin
	pnts_get_cnt := area_pnts_cnt;
end;

// Получение максимального количества точек области рисования
function TDArea.pnts_get_cnt_max() :Integer;
begin
	pnts_get_cnt_max := AREA_PNTS_MAX;
end;

// Проверка видимости точки
function TDArea.pnts_get_visible(area_pnt_o: TDAPoint) :Boolean;
begin
	pnts_get_visible := ((area_pnt_o.get_dcoord.X >= 0) and 
						 (area_pnt_o.get_dcoord.X <= area.Width)) and 
				 		((area_pnt_o.get_dcoord.Y >= 0) and 
				 		 (area_pnt_o.get_dcoord.Y <= area.Height)); 
end;

// Конвертирование точки в Декартову систему
function TDArea.pnts_conv(area_pnt_o: TDAPoint) :TDAPoint;
var
	area_tmp_o: TDAPoint;
begin
	area_tmp_o := TDAPoint.init(area_pnt_o.get_rcoord.X,
								area.Height - area_pnt_o.get_rcoord.Y);
	pnts_conv := area_tmp_o;
end;

end.

